#include <stdio.h>//sprintf

#include "CollisionManager.h"

CollisionManager::CollisionManager()
	:mStaticObjectsArray (nullptr)
	,mDynamicObjectsArray(nullptr)
	,mDynamicArraySize	 (0)
	,mStaticArraySize	 (0)
{}


CollisionManager::~CollisionManager()
{
	deleteStaticObjArray();
	deleteDynamicObjArray();
}


void CollisionManager::copyArray(GameEntity** &arrayTo, GameEntity** arrayFrom, int numElements)
{
	deleteArray(arrayTo);

	arrayTo = new GameEntity*[numElements];
	for(int i = 0; i < numElements; i++)
	{
		arrayTo[i] = arrayFrom[i];
	}
}

void CollisionManager::copyToStaticObjArray(CollectableItemsList* itemsList)
{
	deleteStaticObjArray();

	mStaticArraySize    = itemsList->numAllCandies();
	mStaticObjectsArray = new GameEntity*[itemsList->numAllCandies()];

	for(int i = 0; i < itemsList->numAllCandies(); i++)
	{
		if(i < itemsList->numCandies())
			mStaticObjectsArray[i] = itemsList->mCandies[i];//I'm assuming itemsList->mCandies is already ordered; but it is!
		else
		{
			Candy* specCandy = itemsList->mSpecialCandies[i-itemsList->numCandies()];
			int prevItemIndex = i-1;
			while(prevItemIndex > -1 && specCandy->getTileIndex() < mStaticObjectsArray[prevItemIndex]->getTileIndex())
			{
				--prevItemIndex;
			}
			++prevItemIndex;

			insertInArray(specCandy, prevItemIndex, false);
		}
	}
}
void CollisionManager::copyToDynamicObjArray(GameEntity** objArray, int numElements, GameEntity* nPlayer)
{
	deleteArray(mDynamicObjectsArray);

	mDynamicObjectsArray = new GameEntity*[numElements+1];
	for(int i = 0; i < numElements; i++)
	{
		mDynamicObjectsArray[i] = objArray[i];
	}
	mDynamicObjectsArray[numElements] = nPlayer;
	mDynamicArraySize = numElements+1;
}

void CollisionManager::copyToDynamicObjArray(GameEntity** itemsArray, int numElements)
{
	copyArray(mDynamicObjectsArray, itemsArray, numElements);
	mDynamicArraySize = numElements;
}
void CollisionManager::copyToStaticObjArray (GameEntity** itemsArray, int numElements)
{
	copyArray(mStaticObjectsArray, itemsArray, numElements);
	mStaticArraySize = numElements;
}


void CollisionManager::deleteArray(GameEntity** &array)
{
	if(array != nullptr)
		delete array;
}

void CollisionManager::deleteStaticObjArray() {	mStaticArraySize = 0; deleteArray(mStaticObjectsArray); }
void CollisionManager::deleteDynamicObjArray(){	mDynamicArraySize= 0; deleteArray(mDynamicObjectsArray);}


GameEntity* CollisionManager::getStaticObj(int objIndex)
{
	if(mStaticObjectsArray != nullptr && objIndex < mStaticArraySize)
		return mStaticObjectsArray[objIndex];
	else
		return nullptr;
}
GameEntity* CollisionManager::getDynamicObj(int objIndex)
{
	if(mDynamicObjectsArray != nullptr && objIndex < mDynamicArraySize)
		return mDynamicObjectsArray[objIndex];
	else
		return nullptr;
}

//false if the insertion failed
bool CollisionManager::insertInArray(GameEntity* newEntity, int pos, GameEntity** &arrayTo, int arraySize)
{
	if(pos < arraySize && pos >= 0)
	{
		GameEntity* tmpEntity	  = nullptr, 
				  * shiftedEntity = nullptr;

		for(int i = pos; i < arraySize + 1; ++i)// =( I wanna cry for this!
		//insert newEntity in arrayTo[pos] and shift (of 1 position) everything from pos to arraySize
		{
			if(i < arraySize)
			{
				if(arrayTo[i] != nullptr)
					tmpEntity = arrayTo[i];
				if(shiftedEntity != nullptr)
					arrayTo[i] = shiftedEntity;
				else
					arrayTo[i] = newEntity;
				shiftedEntity = tmpEntity;
			}
			else
			{
				return true;
			}
		}
	}
	return false;
}

bool CollisionManager::insertInArray(GameEntity* newEntity, int pos, bool isDynamic)
{
	int arraySize; 
	GameEntity** tmpArray;
	if(isDynamic)
	{
		arraySize = mDynamicArraySize; 
		tmpArray  = mDynamicObjectsArray;
	}
	else
	{
		arraySize = mStaticArraySize;
		tmpArray  = mStaticObjectsArray;
	}
	return insertInArray(newEntity, pos, tmpArray, arraySize);

	//////////////////////////////////////////////////////////////////
	//the following is the previous version coded in  CollisionManager::setStaticObjArray
	//////////////////////////////////////////////////////////////////

	//Candy* candyToShift = reinterpret_cast<Candy*>(mStaticObjectsArray[prevItem]);
	//mStaticObjectsArray[prevItem] = specCandy;

	//Candy* tmpCandy;//I could use here specCandy but trying to not mess up the readability I choose to declare a new ptr
	//for(++prevItem; prevItem <= i; ++prevItem) 
	//{
	//	tmpCandy = reinterpret_cast<Candy*>(mStaticObjectsArray[prevItem]);
	//	mStaticObjectsArray[prevItem] = candyToShift;
	//	candyToShift = tmpCandy;
	//}
}

void CollisionManager::update(MyScene* scene)
{
	GameEntity* dynEntity,* dynEntityB;
	//char buffer[256];

	for(int i = 0; i < mDynamicArraySize; ++i)
	{
		dynEntity = mDynamicObjectsArray[i];

		if(dynEntity->isAPacMan()) {
			GameEntity* statEntity = getStaticObjCollidingWith(dynEntity);
			if(statEntity != nullptr)
			{
				if(statEntity->getType() == SPECIAL_CANDY) scene->setToSpecial();

				statEntity->collisionWith(dynEntity);
				dynEntity ->collisionWith(statEntity);

				scene->decrementRemainCandies();
				/*sprintf(buffer, "PacMan-Candy collision in tile %i\n", dynEntity->getTileIndex());
				OutputDebugStringA(buffer); */
			}
		}
		for(int j = i+1; j < mDynamicArraySize; ++j)
		{
			dynEntityB = mDynamicObjectsArray[j];
			if(dynEntityB->isAPacMan() != dynEntity->isAPacMan())
			{
				if(areColliding(dynEntity, dynEntityB)){

					dynEntity ->collisionWith(dynEntityB, scene->isSpecialActive());
					dynEntityB->collisionWith(dynEntity,  scene->isSpecialActive());

					/*sprintf(buffer, "PacMan-Ghost collision in tile %i\n", dynEntity->getTileIndex());
					OutputDebugStringA(buffer); */
				}
			}
		}
	}
}

GameEntity* CollisionManager::getStaticObjCollidingWith(GameEntity* entity)
{
	int entityIndex = entity->getTileIndex();
	GameEntity* currEntity;

	int startPos = 0,
		middlePos = mStaticArraySize/2,
		remain = 0;


	while(startPos+middlePos >= 0 && startPos+middlePos < mStaticArraySize && 
		  !areColliding(entity, mStaticObjectsArray[startPos+middlePos]))
	{
		currEntity = mStaticObjectsArray[startPos+middlePos];

		if(entityIndex < currEntity->getTileIndex())
		{
			if(startPos+middlePos == 0)					 { 	return nullptr;	}
		}
		else
		{
			if(startPos+middlePos == mStaticArraySize-1) {	return nullptr;	}
			startPos += middlePos;
		}
		remain += middlePos%2;
		middlePos = middlePos/2 + (remain/2)*(1 - remain%2);
		remain = remain%2;

		if(middlePos == 0) 
		{
			if(remain != 0 && areColliding(entity, mStaticObjectsArray[startPos+middlePos]))
			{	
				return mStaticObjectsArray[startPos+middlePos];	
			}
			return nullptr;
		}
	}
	if (areColliding(entity, mStaticObjectsArray[startPos+middlePos]))
		return mStaticObjectsArray[startPos+middlePos];

	return nullptr;
}

bool CollisionManager::areColliding(GameEntity* entityA, GameEntity* entityB)
{
	if(entityA->isActive() && entityB->isActive())
		return entityA->getTileIndex() == entityB->getTileIndex();
	else
		return false;
}